//
//  PSHashSetTests.m
//  PrefCore
//
//  Created by Sergey on 5/12/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import <XCTest/XCTest.h>
#import "PSHashSet.h"
#import "PSNode.h"
#import <libkern/OSAtomic.h>

@interface CountedNode : PSNode

@end

static int32_t totalCount = 0;

@implementation CountedNode

-(instancetype)init {
    if (self = [super init]) {
        OSAtomicIncrement32(&totalCount);
    }
    return self;
}

-(void)dealloc {
    OSAtomicDecrement32(&totalCount);
}

@end

@interface PSHashSetTests : XCTestCase {

    PSObjectPool *p;

    PSHandHash h[9];
}

@end

@interface GraphDataPoint : NSObject
@property NSDate *date;
@property NSArray *value;
-(instancetype)initWithVal:(NSArray*)val;
@end

@implementation GraphDataPoint

-(instancetype)initWithVal:(NSArray*)val {
    if (self = [super init]) {
        _value = val;
    }
    return self;
}

@end


@implementation PSHashSetTests

-(void)setUp {
    XCTAssertEqual(0, totalCount);
    p = [CountedNode pool];
    [[CountedNode pool] setCapacity:100];
    XCTAssertEqual(100, totalCount);
    for (int i = 0 ; i != 9 ; i++) {
        h[i] = PSHandAddCard(0, [PSCard cardWithIndex:i]);
    }
}

-(void)tearDown {
    [p setCapacity:0];
}

-(void)testCreate {
    PSHashSet *hs = [[PSHashSet alloc] init];
    XCTAssertNotNil(hs);
}

-(void)testAddAndRetrieve {
    PSHashSet *hs = [[PSHashSet alloc] init];
    PSTreeLevel *lvl = [[PSTreeLevel alloc] initWithRound:123];
    PSNode *a = [PSNode nodeWithLevel:lvl starter:1 hand0:h[0] hand1:h[1] hand2:h[2]];
    XCTAssertNotNil(a);
    [hs put:a];
    PSNode *key = [CountedNode keyForPlayer:1 andHands:h];
    PSNode *back = [hs get:key];
    XCTAssertEqual(a, back);
    PSNode *notThereKeyOne = [CountedNode keyForPlayer:2 andHands:h];
    PSNode *notThere = [hs get:notThereKeyOne];
    PSNode *notThereKeyTwo = [CountedNode keyForPlayer:1 andHands:h+3];
    notThere = [hs get:notThereKeyTwo];
    XCTAssertNil(notThere);
    lvl = nil;
    hs = nil;
    [[CountedNode pool] putObject:a];
}

-(void)testClear {
    PSHashSet *hs = [[PSHashSet alloc] init];
    PSTreeLevel *lvl = [[PSTreeLevel alloc] initWithRound:123];
    PSNode *a = [PSNode nodeWithLevel:lvl starter:1 hand0:h[0] hand1:h[1] hand2:h[2]];
    [hs put:a];
    PSNode *b = [PSNode nodeWithLevel:lvl starter:2 hand0:h[3] hand1:h[4] hand2:h[5]];
    [hs put:b];
    PSNode *c = [PSNode nodeWithLevel:lvl starter:2 hand0:h[6] hand1:h[7] hand2:h[8]];
    [hs put:c];
    PSNode *aKey = [PSNode keyForPlayer:1 andHands:h];
    XCTAssertEqual(a, [hs get:aKey]);
    PSNode *bKey = [PSNode keyForPlayer:2 andHands:h+3];
    XCTAssertEqual(b, [hs get:bKey]);
    PSNode *cKey = [PSNode keyForPlayer:2 andHands:h+6];
    XCTAssertEqual(c, [hs get:cKey]);
    [hs clear];
    aKey = [PSNode keyForPlayer:1 andHands:h];
    XCTAssertNil([hs get:aKey]);
    bKey = [PSNode keyForPlayer:2 andHands:h+3];
    XCTAssertNil([hs get:bKey]);
    cKey = [PSNode keyForPlayer:2 andHands:h+6];
    XCTAssertNil([hs get:cKey]);
    lvl = nil;
    hs = nil;
    [[CountedNode pool] putObject:a];
    [[CountedNode pool] putObject:b];
    [[CountedNode pool] putObject:c];
}

-(void)testRemove {
    PSHashSet *hs = [[PSHashSet alloc] init];
    PSTreeLevel *lvl = [[PSTreeLevel alloc] initWithRound:123];
    PSNode *a = [PSNode nodeWithLevel:lvl starter:1 hand0:h[0] hand1:h[1] hand2:h[2]];
    [hs put:a];
    PSNode *b = [PSNode nodeWithLevel:lvl starter:2 hand0:h[3] hand1:h[4] hand2:h[5]];
    [hs put:b];
    PSNode *c = [PSNode nodeWithLevel:lvl starter:2 hand0:h[6] hand1:h[7] hand2:h[8]];
    [hs put:c];
    PSNode *aKey = [PSNode keyForPlayer:1 andHands:h];
    XCTAssertEqual(a, [hs get:aKey]);
    [hs remove:a];
    XCTAssertNil([hs get:aKey]);
    PSNode *bKey = [PSNode keyForPlayer:2 andHands:h+3];
    XCTAssertEqual(b, [hs get:bKey]);
    [hs remove:b];
    XCTAssertNil([hs get:bKey]);
    PSNode *cKey = [PSNode keyForPlayer:2 andHands:h+6];
    XCTAssertEqual(c, [hs get:cKey]);
    [hs remove:c];
    XCTAssertNil([hs get:cKey]);
    lvl = nil;
    hs = nil;
    [[CountedNode pool] putObject:a];
    [[CountedNode pool] putObject:b];
    [[CountedNode pool] putObject:c];
}

-(void)testHashCollisions {
    PSHandHash h1[] = {0, 100, 200};
    PSHandHash h2[] = {31, 100, 200};
    PSHashSet *hs = [[PSHashSet alloc] init];
    PSTreeLevel *lvl = [[PSTreeLevel alloc] initWithRound:123];
    PSNode *a = [PSNode nodeWithLevel:lvl starter:1 hand0:h1[0] hand1:h1[1] hand2:h1[2]];
    [hs put:a];
    PSNode *b = [PSNode nodeWithLevel:lvl starter:0 hand0:h2[0] hand1:h2[1] hand2:h2[2]];
    [hs put:b];
    [hs remove:a];
    [hs clear];
    [hs put:b];
    [hs put:a];
    [hs remove:a];
    lvl = nil;
    hs = nil;
    [[CountedNode pool] putObject:a];
    [[CountedNode pool] putObject:b];
}

@end
